package compiler.patch;

import java.awt.Component;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import newcrunch.util.Misc;
import newcrunch.util.PropertyLoader;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
/**
 * Class specifically created to download / move tools.jar to resolve a bug
 * reported at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7181951
 * @author WL
 */
public class CompilerChecker 
{
    private String javaVersion;
    private String jrePath;
    private String correctLocation;
    private String wrongLocation;
    private String wrongPath;
    private String correctPath;
    private static CompilerChecker instance;
    private String libraryLocation = ".";
    private String downloadURL = "http://newcrunch.comp.nus.edu.sg/newcrunch/patch.php";
    private HttpClient httpClient = new DefaultHttpClient();
    private int bufferSize = 1024 * 1024; //1MB buffer
    private boolean continueDownload;
    
    private CompilerChecker() 
    {
        try {
            libraryLocation = PropertyLoader.getLibraryPath();
            
            javaVersion = WinRegistry.readString (
                WinRegistry.HKEY_LOCAL_MACHINE,                             //HKEY
            "SOFTWARE\\JavaSoft\\Java Runtime Environment",           //ValueName
            "CurrentVersion");                                    //ValueName
            
            jrePath = WinRegistry.readString (
                    WinRegistry.HKEY_LOCAL_MACHINE,                             //HKEY
                "SOFTWARE\\JavaSoft\\Java Runtime Environment\\" + javaVersion,           //Key
                "JavaHome");     
            
            correctPath = jrePath + "\\lib";
            wrongPath = jrePath + "\\lib\\ext";
            correctLocation = correctPath + "\\tools.jar";
            wrongLocation = wrongPath + "\\tools.jar";
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
        }        
    }
    
    public static CompilerChecker getInstance()
    {
        if (instance == null)
            instance = new CompilerChecker();
        
        return instance;
    }
    
    public void setLibraryLoc(String loc)
    {
        libraryLocation = loc;
    }
    
    public void setDownloadURL(String URL)
    {
        downloadURL = URL;
    }
    
    public boolean hasToolsJarInLib()
    {
        return  new File(libraryLocation + "\\tools.jar").exists();
    }
    
    public boolean hasToolsJar()
    {
        boolean exists = new File(correctLocation).exists();
        
        if (!exists)
            exists = new File(libraryLocation + "\\tools.jar").exists();
        
        return exists;
    }
    
    private boolean toolsJarWrongDir()
    {
        File file = new File(wrongLocation);
        return file.exists();
    }
    
    private boolean moveJarToCorrectDir()
    {
        try {
            FileInputStream inStream = new FileInputStream(wrongLocation);
            FileOutputStream outStream = new FileOutputStream(correctLocation);
            
            File file = new File(correctLocation);
            file.createNewFile();
            
            byte[] buffer = new byte[1024];
            int read = 0;
            while ((read = inStream.read(buffer)) > 0)
                outStream.write(buffer, 0, read);
            
            outStream.close();
            inStream.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        } catch (IOException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        
        return true;
    }
    
    private boolean isWindows()
    {
        String os = System.getProperty("os.name");
        return os.startsWith("Windows");
    }
    
    private boolean showConfirmPane(Component parent, String message, String title)
    {
        return JOptionPane.showConfirmDialog(parent, message, title, JOptionPane.OK_CANCEL_OPTION) == 
                JOptionPane.OK_OPTION;
    }
    
    private boolean downloadToolsJar()
    {
        if (!showConfirmPane(null, "tools.jar is not found in your system. \n Proceed to download it?", 
                "Download confirmation"))
            return false;
        
        File file = null;
        //try to save it to the correct location first
        try {
            file = new File(correctLocation);
            file.createNewFile();
        } catch (IOException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
            
            //if not possible, save it under the library directory
            file = new File(libraryLocation + "\\tools.jar");
            try {
                file.createNewFile();
            } catch (IOException ex1) {
                Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex1);
                return false;
            }
        }
        
        if (file != null && file.exists())
        {
            try {
                return download(downloadURL, file);
            }
            catch (IOException ex) {
                JOptionPane.showMessageDialog(null, "Error downloading tools.jar", "Error", JOptionPane.ERROR_MESSAGE);
                if (file.exists())
                    file.delete();
                
                Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
                return false;
            }
        }
        
        return true;
    }
    
    private boolean download(String url, final File saveFile) throws IOException
    {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("version", javaVersion));

        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");

        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(entity);
        final HttpResponse response = httpClient.execute(httpPost);

        Header[] headerArr = response.getHeaders("Content-Length");
        int len = 0;
        if (headerArr != null && headerArr.length > 0)
            len = Integer.parseInt(headerArr[0].getValue());

        final DownloadProgress downloadProg = new DownloadProgress();
        downloadProg.setTotal(len);

        final Object lock = new Object();

        final SwingWorker<Object, Void> worker = new SwingWorker<Object, Void>()
        {
            private OutputStream out;
            
            @Override
            protected Object doInBackground(){
                synchronized (lock)
                {
                    downloadProg.setVisible(true);
                    continueDownload = true;

                    int read = 0;
                    try {
                        final InputStream inputStream = response.getEntity().getContent();
                        out = new FileOutputStream(saveFile);
                        int curr = -1;
                        byte[] buf = new byte[bufferSize];
                        try {
                            while ((curr = inputStream.read(buf)) != -1 && continueDownload)
                            {
                                out.write(buf, 0, curr);
                                read += curr;
                                downloadProg.setCurrent(read);
                            }
                        } catch (IOException exp) {
                            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, exp);
                        } finally {
                            out.close();
                            
                            //deletes the partially downloaded file
                            if (!downloadProg.hasCompleted() && saveFile.exists() && 
                                    saveFile.canWrite())
                                saveFile.delete();
                            
                            inputStream.close();
                        }
                    } catch (IOException e) {
                        Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, e);
                    } catch (Exception e) {
                        Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, e);
                    }
                    
                    lock.notifyAll();
                }
                return null;
            }
            
            @Override
            protected void done()
            {
                continueDownload = false;
                downloadProg.setVisible(false);
                downloadProg.dispose();
            }
        };
        
        downloadProg.addWindowListener(new WindowAdapter(){
            @Override
            public void windowClosing(WindowEvent e)
            {
                if (!downloadProg.hasCompleted())
                {
                    if (saveFile.exists() && saveFile.canWrite())
                    {
                        try {
                            worker.cancel(true);
                        } catch (Exception ex) {
                            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            }
        });        

        worker.execute();
        
        try {
            synchronized (lock)
            {
                lock.wait();
            }
        }
        catch (InterruptedException ex) {
            Logger.getLogger(CompilerChecker.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        
        return downloadProg.hasCompleted();
    }    
    
    public boolean check()
    {
        if (!isWindows())
            return true;
        
        if (hasToolsJar())
            return true;
        
        if (toolsJarWrongDir())
        {
            if (showConfirmPane(null, "In your Java installation, tools.jar is found at " + 
                wrongPath + " \nbut it should be placed at " + 
                correctPath + " due to bug 7181951 in Java. \n "
                + "Would you like to move it to the correct location for compiler to work?", 
                "Move confirmation"))
                return moveJarToCorrectDir();
            else
                return false;
        }
        
        if (downloadToolsJar())
        {
            if (CustomToolProvider.getSystemJavaCompiler() == null && showConfirmPane(null, "Restart required for changes to take effect. Restart now?", "Restart Confirmation"))
                Misc.restartApplication();
        }
        
        return false;
    }
}
